
function getTypes(f)
    local list = {};
    if(f==nil)then f = ""; end;
    local len = #f;
    for k,v in ipairs(record_type) do
        local decl = v:GetDecl();
        local name = decl:GetRecordTypeName();
        if(name and name:sub(1,len)==f)then
            table.insert( list, decl );
        end
    end
    for k,v in ipairs(union_type) do
        local decl = v:GetDecl();
        local name = decl:GetUnionTypeName();
        if(name and name:sub(1,len)==f)then
            table.insert( list, decl );
        end
    end

    return list;
end

function getCmpExpr(s,s2)
    return string.format('strcmp(%s,"%s")',s,s2);
end

space = "  "

function outBSearch(out,low,high,list,returnList)
    local mid = high - low;
    if(mid~=0)then
        local rvalue = mid & 1;
        mid = (mid >> 1) + low;
        if(rvalue~=0)then
            mid = mid + 1;
        end
        local s = "%sif((%s = %s)==0)\n  %s%s;\n"
        local cmpv = "cmpv";
        local cmp = getCmpExpr("key",list[mid]);
        local rtn = string.format('%s',returnList[mid]);
        s = string.format(s,space,cmpv,cmp,space,rtn);
        out:write(s);
        if(mid-low~=1)then
            out:write(string.format("%selse if(%s<0){\n",space,"cmpv"));
            space = "  " .. space;
            outBSearch(out,low,mid-1,list,returnList);
            space = space:sub(1,#space-2);
            out:write(space.."}\n");
        end
        if(high-mid~=0)then
            
            out:write(string.format("%selse if(%s>0){\n",space,"cmpv"));
            space = "  " .. space;
            outBSearch(out,mid,high,list,returnList);
            space = space:sub(1,#space-2);
            out:write(space.."}\n");
        end
    end
end

function exportType(f,out)
    local types = getTypes(f);
    local typeFuncOut = io.tmpfile();
    local inlineFunc = io.tmpfile();
    local metatableOut = io.tmpfile();
    local setmetaOut = io.tmpfile();
    -- table.sort(types,function(a,b)
    --     return a:GetRecordTypeName()<b:GetRecordTypeName();
    -- end);
    local nameTable = {};
    for k,v in ipairs(types) do
        local name = v:GetTypeName();
        if(v:IsAliasStruct())then
            nameTable[name] = v;
        elseif(v:IsAliasUnion())then
            nameTable[name] = v;
        elseif(nameTable[name]==nil)then 
            nameTable[name] = v;
        elseif(v:FieldExists() and nameTable[name]==nil)then
            nameTable[name] = v;
        end
    end
    local type;

    setmetaOut:write([[
static int lTypeInit(lua_State*L){
]]);

    for k,v in ipairs(types) do
        local name = v:GetTypeName();
        v = nameTable[name];
        nameTable[name] = nil;
        if(v)then
            typeName = v:GetTypeName();
            local structName = typeName;
            if(v:IsAliasStruct() or v:IsAliasUnion())then
                structName = structName;
            else
                if(structName=="SDL_Event")then print(v.id);end;
                if(v:IsUnionType())then
                    structName = 'union '..structName;
                else
                    structName = 'struct '..structName;
                end
            end
            --out:write(name..'{\n');

            --获得所有字段名
            local fields = {};
            local fieldsName = {};

            
            local next,name,type,bpos,size,algn = v:GetTypeNextFieldInfo();
            while(name)do
                --out:write(name..":"..string.format('%d:%d\n',bpos,size));
                fields[name] = { name= name,type = type, bpos = bpos,size = size,algn = algn};
                table.insert(fieldsName,name);
                if(next==nil)then break;end;
                next,name,type,bpos,size,algn = next:GetTypeNextFieldInfo();
            end

            table.sort(fieldsName, function(a,b) return a<b;  end);
            typeFuncOut:write(string.format([[
INLINE int inlineTKey%s(lua_State*L,const char*key){
  %s *self = lcheck_toptr(L,1);
  int cmpv;
]],
typeName,structName));
    if(#fieldsName~=0)then
            metatableOut:write(string.format([[
static int lT%sget(lua_State*L){
  if(lua_isstring(L,2)){
    return inlineTKey%s(L,lua_tostring(L,2));
  }
  else if(lua_isinteger(L,2)){
    int idx = lua_tointeger(L,2);
    %s *self = (%s*)lcheck_toptr(L,1);
    self += idx;
    lua_pushlightuserdata(L,self);
    luaL_setmetatable(L,"%s");
    return 1;
  }
  else{
    luaL_argerror(L,2,"index error type");
  };
  return 0;
}
]],
typeName,typeName,structName,structName,typeName
));
    else
        metatableOut:write(string.format([[
static int lT%sget(lua_State*L){
  luaL_argerror(L,2,"field not exist");
  return 0;
}
]],typeName));
    end;

    local siz = string.format('sizeof(%s)',structName);
    if(#fieldsName==0)then
        siz = '0';
    end

            setmetaOut:write(string.format([[
  if(luaL_newmetatable(L,"%s")){
    lua_pushcfunction(L,lT%sget);
    lua_setfield(L,-2,"__index");
    lua_pushcfunction(L,inlineTKeySet%s);
    lua_setfield(L,-2,"__newindex");
    lua_pushstring(L,"%s");
    lua_setfield(L,-2,"name");
    lua_pushinteger(L,%s);
    lua_setfield(L,-2,"size");
    lua_pop(L,1);
  }
]],typeName,typeName,typeName,typeName,siz
));

            local returnList = {};
            local setList = {};
            for k,vv in ipairs(fieldsName) do
                local v = fields[vv].type;
                --if(v:IsIntegerType())then
                local fn = string.format('inlineT%s_get%s',typeName,vv);
                returnList[k] = string.format("return %s(L,self)",fn);
                
                local pushret = "";
                local setV = string.format('self->%s',fields[vv].name);
                local setRV = "";
                local setVV;
                if(v:IsIntegerType())then
                    pushret = string.format('lua_pushinteger(L,self->%s)',fields[vv].name);
                    setRV = 'luaL_checkinteger'
                elseif(v:IsPointerType())then
                    local type = v:UnPointerType();
                    setRV = 'lcheck_toptr'
                    if(type:IsRecordType() or type:IsUnionType())then
                        print(fn,fields[vv].type.id);
                        pushret = string.format('*(void**)lua_newuserdata(L,sizeof(void**)) = (void*)self->%s;luaL_setmetatable(L,"%s")',fields[vv].name,type:GetTypeName());
                    elseif(type:IsIntegerType())then
                        pushret = string.format('*(void**)lua_newuserdata(L,sizeof(void**)) = (void*)self->%s',fields[vv].name);
                    elseif(type:IsVoid())then
                        pushret = string.format('*(void**)lua_newuserdata(L,sizeof(void**)) = (void*)self->%s',fields[vv].name);
                    end
                elseif(v:IsRealType())then
                    setRV = 'luaL_checknumber'
                    pushret = string.format('lua_pushnumber(L,self->%s)',fields[vv].name);
                elseif(v:IsRecordType() or v:IsUnionType())then
                    local fieldTypeName = v:GetTypeName();
                    if(fieldTypeName:sub(1,1)=='.')then
                        local name = fields[vv].name;
                        setVV = string.format("MCopy(&self->%s,lcheck_toptr(L,3),sizeof(self->%s))",name,name)
                    else
                      setRV = string.format('*(%s*)lcheck_toptr',fieldTypeName);
                      pushret = string.format('*(void**)lua_newuserdata(L,sizeof(void**)) = (void*)&self->%s;luaL_setmetatable(L,"%s")',fields[vv].name,fieldTypeName);
                    end
                else
                    local name = fields[vv].name;
                    setVV = string.format("MCopy(&self->%s,lcheck_toptr(L,3),sizeof(self->%s))",name,name)
                end;
                if(setVV)then
                    setList[k] = setVV;
                else 
                    setList[k] = string.format('%s = %s(L,3)',setV,setRV);
                end
                local func = string.format([[
INLINE int %s(lua_State*L,%s*self){
  %s;
  return 1;
};
]],fn,typeName,pushret);
                inlineFunc:write(func);
                --end
            end
            outBSearch(typeFuncOut,0,#fieldsName,fieldsName,returnList);
            typeFuncOut:write('  return 0;\n};\n')

            typeFuncOut:write(string.format([[
INLINE int inlineTKeySet%s(lua_State*L){
  %s *self = lcheck_toptr(L,1);
  const char *key = lua_tostring(L,2);
  int cmpv;
]],
                typeName,structName));
            outBSearch(typeFuncOut,0,#fieldsName,fieldsName,setList);
            typeFuncOut:write('  return 0;\n};\n')

            

            --out:write('};')
        end
    end
    
    setmetaOut:write('}\n');

    typeFuncOut:seek("set");
    inlineFunc:seek('set');
    metatableOut:seek'set';
    setmetaOut:seek'set';

    out:write(inlineFunc:read('*a'));
    out:write(typeFuncOut:read('*a'));
    out:write(metatableOut:read('*a'));
    out:write(setmetaOut:read('*a'));

    typeFuncOut:close();
end


